local jass = require 'jass.common'
local code = require 'jass.code'
local japi = require 'jass.japi'
local math_min = math.min
local math_max = math.max
local math_floor = math.floor

local JSetUnitState = japi.SetUnitState
local JGetUnitState = japi.GetUnitState

local function SetUnitState(handle, id, value)
	return JSetUnitState(handle, jass.ConvertUnitState(id), value)
end

local function GetUnitState(handle, id)
	return JGetUnitState(handle, jass.ConvertUnitState(id))
end

local attribute = {


	['冷却缩减'] = true,
	['技能伤害'] = true,
	['护甲穿透'] = true,
	['护甲穿透%'] = true,

	--属性类型

	['护盾'] = true,

	['生命'] = true,
	['魔法'] = true,
	['当前生命'] = true,
	['当前魔法'] = true,
	['生命加成'] = true,
	['魔法加成'] = true,

	['动画伤害点'] = true,
	['动画恢复点'] = true,


	['基础攻击'] = true,
	['骰子数量'] = true, 	-- 不建议装备拥有这个属性
	['骰子面数'] = true, 	-- 不建议装备拥有这个属性

	['基础护甲'] = true,

	['额外攻击'] = true,
	['额外护甲'] = true,

	['攻击加成'] = true,
	['护甲加成'] = true,

	['攻击距离'] = true,
	['索敌范围'] = true,

	['攻击速度'] = true,
	['攻速收益'] = true,
	['攻击间隔'] = true,

	['移动速度'] = true,
	['移动速度%'] = true, -- 移动速度的收益 给100 相当于移动速度翻1倍

	['吸血'] = true, -- 物理伤害会触发，

	['伤害增幅'] = true,
	['伤害减免'] = true, -- 范围：0-100%，最多抵挡80%的伤害

	['控制减免'] = true, -- 减少受到的控制效果的持续时间


	['生命恢复'] = true,
	['魔法恢复'] = true,

	['治疗效果'] = true,

	['额外视野'] = true, -- 1800 max
	['异常伤害'] = true,
	['单位体型'] = true,

}


ac.attribute = attribute

local set = {}
local get = {}
local on_add = {}
local on_set = {}
local on_get = {}

local mt = ac.unit.__index

--设置单位属性
function mt:set(name, value)
	if not attribute[name] then
		log.error('错误的属性名', name)
		return false
	end
	local state = self.attributes
	if not state[name] then
		state[name] = get[name] and get[name](self) or 0
	end

	local reset
	if on_set[name] then
		reset = on_set[name](self, value)
	end

	state[name] = value

	if set[name] then
		set[name](self, value)
	end

	if reset then
		reset()
	end

end

--获取单位属性
function mt:get(name)
	if not attribute[name] then
		log.error('错误的属性名', name)
		return 0
	end
	local state = self.attributes

	if not state[name] then
		state[name] = get[name] and get[name](self) or 0
	end

	local value = state[name]
	local reget

	if on_get[name] then
		reget = on_get[name](self, value)
	end

	if reget then
		return reget
	end

	return value
end

--增加单位属性
function mt:add(name, value)
	if not attribute[name] then
		log.error('错误的属性名', name)
		return false
	end
	local readd
	local basic = self:get(name)
	if on_add[name] then
		readd = on_add[name](self, basic, value)
	end
	self:set(name, basic + value)
	if readd then
		readd()
	end
end

local Stag = string.unpack('>I4', '+Agi')
local Stst = string.unpack('>I4', '+Str')
local Stin = string.unpack('>I4', '+Int')
local Stdf = string.unpack('>I4', '+Amr')
local Stak = string.unpack('>I4', '+Atk')

get['当前生命'] = function(self)
	return jass.GetWidgetLife(self.handle)
end

on_get['当前生命'] = function(self, life)
	if self:has_ability 'Abld' then
		return jass.GetWidgetLife(self.handle)
	end
end

on_set['当前生命'] = function(self, value)
	local cur_life = value
	local max_life = self:get_life_max()
	local min_life = 1
	if cur_life > max_life then
		cur_life = max_life
	elseif cur_life < min_life then
		cur_life = min_life
	end
	return function()
		jass.SetWidgetLife(self.handle, cur_life)
		self.attributes['当前生命'] = cur_life
		self:notify("单位-生命值变化", self)
	end
end

get['生命'] = function(self)
	return japi.GetUnitState(self.handle, jass.ConvertUnitState(1)) * 1
end

on_get['生命'] = function(self, life)
	if self:has_ability 'Abld' then
		return japi.GetUnitState(self.handle, jass.ConvertUnitState(1)) * 1
	end
end

set['生命'] = function(self, value)
	japi.SetUnitState(self.handle, jass.ConvertUnitState(1), ((value * (self:get('生命加成') + 100) * 0.01)))
end

on_set['生命'] = function(self)
	local percent = self:get_life_percentage()
	return function()
		self:set("当前生命", self:get_life_max() * percent)
	end
end


set['生命加成'] = function(self, value)
	japi.SetUnitState(self.handle, jass.ConvertUnitState(1), (self:get('生命') * (value + 100) * 0.01))
end

on_set['生命加成'] = function(self, value)
	local percent = self:get_life_percentage()
	return function()
		self:set("当前生命", self:get_life_max() * percent)
	end
end

on_get['当前魔法'] = function(self)
	return GetUnitState(self.handle, jass.ConvertUnitState(2)) * 1.0
end

on_set['当前魔法'] = function(self, value)
	local max = self:get_mana_max() 
	local min = 0
	local cur = value
	if cur <= min then 
		cur = min 
	elseif cur >= max then 
		cur = max
	end
	return function()
		SetUnitState(self.handle, jass.ConvertUnitState(2), cur)
	end
end

get['魔法'] = function(self)
	return GetUnitState(self.handle, jass.ConvertUnitState(3) )  * 1
end

on_set['魔法'] = function(self, value)
	local mana = self:get('魔法')
	if mana > 0 and value > 0 then
		local percent = self:get_mana_percentage()
		return function()
			japi.SetUnitState(self.handle, jass.ConvertUnitState(3), (value * (self:get('魔法加成') + 100) / 100))
			self:set("当前魔法", self:get_mana_max() * percent)
		end
	else
		return function()
			japi.SetUnitState(self.handle, jass.ConvertUnitState(3), (value * (self:get('魔法加成') + 100) / 100))
			self:set("当前魔法", math.min(mana, value))
		end
	end
end

on_set['魔法加成'] = function(self, value)
	if self:get('魔法') > 0 then
		local percent = self:get_mana_percentage()
		return function()
			japi.SetUnitState(self.handle, jass.ConvertUnitState(3), (self:get('魔法') * (value + 100) / 100) * 100 // 100)
			self:set('当前魔法', self:get_mana_max() * percent)
		end
	end
end


on_set['护盾'] = function(self, value)
	value = math.max(value, 0)
	return function()
		self.attributes['护盾'] = value
	end
end

get['基础攻击'] = function(self)
	return GetUnitState(self.handle, 0x12) * 1
end

set['基础攻击'] = function(self, value)
	SetUnitState(self.handle, 0x12, value)
end

on_set['基础攻击'] = function(self, value)
	local current = self:get("基础攻击")
	local change = value - current
	return function()
		self:add("额外攻击", change * self:get("攻击加成") * 0.01)
	end
end

set['额外攻击'] = function(self, value)
	jass.SetUnitAbilityLevel(self.handle, Stak, 2)
	japi.EXSetAbilityDataReal(japi.EXGetUnitAbility(self.handle, Stak), 1, 108, value)
	jass.SetUnitAbilityLevel(self.handle, Stak, 1)
end

on_set['攻击加成'] = function(self, value)
	local current = self:get("攻击加成")
	local change = value - current
	return function()
		self:add("额外攻击", self:get("骰子数量") * self:get("骰子面数") * current * 0.01 * -1 )
		self:add("额外攻击", self:get("骰子数量") * self:get("骰子面数") * value   * 0.01 )
		self:add("额外攻击", self:get("基础攻击") * change * 0.01)
	end
end

set['骰子数量'] = function(self, value)
	SetUnitState(self.handle, 0x10, value)
end

on_set['骰子数量'] = function(self, value)
	local current = self:get("骰子数量")
	local change = value - current
	return function()
		self:add("额外攻击", current * self:get("骰子面数") * self:get("攻击加成") * 0.01 * -1) 
		self:add("额外攻击", value   * self:get("骰子面数") * self:get("攻击加成") * 0.01 )
	end
end

get['骰子数量'] = function(self)
	return GetUnitState(self.handle, 0x10) * 1
end

set['骰子面数'] = function(self, value)
	SetUnitState(self.handle, 0x11, value)
end

on_set['骰子面数'] = function(self, value)
	local current = self:get("骰子面数")
	local change = value - current
	return function()
		self:add("额外攻击", current * self:get("骰子数量") * self:get("攻击加成") * 0.01 * -1) 
		self:add("额外攻击", value 	 * self:get("骰子数量") * self:get("攻击加成") * 0.01 )
	end
end

get['骰子面数'] = function(self)
	return GetUnitState(self.handle, 0x11) * 1
end

get['基础护甲'] = function(self)
	local attach = self:get '额外护甲'
	jass.SetUnitAbilityLevel(self.handle, Stdf, 2)
	japi.EXSetAbilityDataReal(japi.EXGetUnitAbility(self.handle, Stdf), 1, 108, 0)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 1)
	local value = GetUnitState(self.handle, 0x20)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 2)
	japi.EXSetAbilityDataReal(japi.EXGetUnitAbility(self.handle, Stdf), 1, 108, attach)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 1)
	return value
end

set['基础护甲'] = function(self, value)
	local attach = self:get '额外护甲'
	jass.SetUnitAbilityLevel(self.handle, Stdf, 2)
	japi.EXSetAbilityDataReal(japi.EXGetUnitAbility(self.handle, Stdf), 1, 108, 0)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 1)
	SetUnitState(self.handle, 0x20, value)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 2)
	japi.EXSetAbilityDataReal(japi.EXGetUnitAbility(self.handle, Stdf), 1, 108, attach)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 1)
end

on_set['基础护甲'] = function(self, value)
	local current = self:get("基础护甲")
	local change = value - current
	return function()
		self:add("额外护甲", change * self:get("护甲加成") * 0.01)
	end
end

set['额外护甲'] = function(self, value)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 2)
	japi.EXSetAbilityDataReal(japi.EXGetUnitAbility(self.handle, Stdf), 1, 108, value)
	jass.SetUnitAbilityLevel(self.handle, Stdf, 1)
end

on_set['护甲加成'] = function(self, value)
	local current = self:get("护甲加成")
	local change = value - current
	return function()
		self:add("额外护甲", self:get("基础护甲") * change * 0.01)
	end
end

get['攻击间隔'] = function(self)
	return japi.GetUnitState(self.handle, jass.ConvertUnitState(0x25)) * 1
end

set['攻击间隔'] = function(self, attack_cool)
	attack_cool = math.max(0.01, attack_cool)
	japi.SetUnitState(self.handle, jass.ConvertUnitState(0x25), attack_cool)
end

-- 攻速
set['攻击速度'] = function(self, speed)
	japi.SetUnitState(self.handle, jass.ConvertUnitState(0x51), 1 + ((speed - 100) * 0.01 * self:get("攻速收益") * 0.01))
end

get['攻击速度'] = function(self)
	return 100
end

-- 攻速收益
set['攻速收益'] = function(self, value)
	japi.SetUnitState(self.handle, jass.ConvertUnitState(0x51), 1 + (self:get('攻击速度') - 100) * 0.01 * value *
	0.01)
end

get['攻速收益'] = function(self)
	return 100
end

get['移动速度%'] = function(self)
	return 100
end

set['移动速度%'] = function(self, value)
	jass.SetUnitMoveSpeed(self.handle, self:get('移动速度') * value * 0.01)
end

get['移动速度'] = function(self)
	return jass.GetUnitMoveSpeed(self.handle) * 1
end

set['移动速度'] = function(self, value)
	jass.SetUnitMoveSpeed(self.handle, value * self:get('移动速度%') * 0.01)
end

set['索敌范围'] = function(self, range)
	jass.SetUnitAcquireRange(self.handle, range)
end

get['索敌范围'] = function(self)
	return jass.GetUnitAcquireRange(self.handle) * 1
end

get['攻击距离'] = function(self)
	return japi.GetUnitState(self.handle, 0x16) * 1
end

set['攻击距离'] = function(self, value)
	japi.SetUnitState(self.handle, 0x16, value)
end

get['动画恢复点'] = function(self)
	return japi.GetUnitState(self.handle, 0x28) * 1
end

set['动画恢复点'] = function(self, value)
	japi.SetUnitState(self.handle, 0x28, value)
end

get['动画伤害点'] = function(self)
	return japi.GetUnitState(self.handle, 0x26) * 1
end

set['动画伤害点'] = function(self, value)
	japi.SetUnitState(self.handle, 0x26, value)
end

on_set['单位体型'] = function(self, value)
	return function()
		self:set_scale(self:get_scale())
	end
end

get['单位体型'] = function(self)
	return 100 
end

local function set_sight(handle, sight)
	local symbol = ((sight) > 0) and 2 or 3
	local integer = math.floor(math.min(math.abs(sight), 1800))
	if integer <= 0 then return end
	for i = 1, 11 do
		if integer % 2 == 1 then
			local code = FourCC(('SY%02X'):format(i))
			jass.UnitAddAbility(handle, code)
			jass.SetUnitAbilityLevel(handle, code, symbol)
			jass.UnitRemoveAbility(handle, code)
		end
		integer = math.floor(integer / 2)
		if integer <= 0 then break end
	end
end

on_set['额外视野'] = function(self, value)
	set_sight(self.handle, -self:get("额外视野"))
	set_sight(self.handle, value)
end

set['生命恢复'] = function(self, value)
	self:refresh_regen()
end 

set['魔法恢复'] = function(self, value)
	self:refresh_regen()
end 

------------------------------------------------------------
----------------------[[ 开放的获取API ]]--------------------
------------------------------------------------------------

-- 获取生命值
function mt:get_life()
	if self.removed or self:is_dead() then
		return 0
	end
	return self:get('当前生命')
end

-- 设置生命值
function mt:set_life(life)
	if self.removed then
		return
	end
	self:set('当前生命', life)
end

-- 增加生命
function mt:add_life(value)
	self:set_life(self:get_life() + value)
end

-- 获取生命百分比
function mt:get_life_percentage()
	return self:get_life() / self:get_life_max()
end

-- 获取护盾百分比
function mt:get_shield_percentage()
	return self:get("护盾") / (self:get_life_max())
end

-- 获取魔法值
function mt:get_mana()
	if self.removed then
		return 0
	end
	return self:get('当前魔法')
end

-- 设置魔法值
function mt:set_mana(mana)
	if self.removed then
		return
	end
	self:set('当前魔法', mana)
end

-- 增加魔法
function mt:add_mana(value)
	self:set_mana(self:get_mana() + value)
end

-- 获取魔法百分比
function mt:get_mana_percentage()
	if self:get_mana_max() > 0 then
		return self:get_mana() / self:get_mana_max()
	end
	return 0.0
end

-- 获取魔法最大值
function mt:get_mana_max()
	return self:get('魔法') * (self:get('魔法加成') + 100) * 0.01
end

-- 获取生命最大值
function mt:get_life_max()
	return self:get('生命') * (self:get('生命加成') + 100) * 0.01
end

-- 获取基础攻击力
function mt:get_common_attack()
	return self:get('基础攻击')
end

-- 获取额外攻击
function mt:get_attach_attack()
	return self:get('额外攻击')
end

-- 获取骰子最小攻击
function mt:get_dice_min()
	return self:get '骰子数量' * 1
end

-- 获取骰子最大攻击
function mt:get_dice_max()
	return self:get '骰子数量' * self:get '骰子面数' * 1
end

-- 获取骰子攻击
function mt:get_dice_attack()
	local min = self:get_dice_min()
	local max = self:get_dice_max()
	local cur = 0
	if min > max then
		return min + 0.01
	end
	return math.random(min, max) + 0.01
end

-- 获取总攻击力
function mt:get_attack()
	return self:get_common_attack() + self:get_dice_attack() + self:get_attach_attack()
end

-- 获取护甲值
function mt:get_common_amor()
	return self:get('基础护甲')
end

function mt:get_attach_amor()
	return self:get('额外护甲')
end

function mt:get_amor()
	return self:get_common_amor() + self:get_attach_amor()
end

-- 获取攻击速度
function mt:get_attack_speed()
	return math.min(self:get('攻击速度'), 500)
end

-- 获取移动速度
function mt:get_move_speed()
	return jass.GetUnitMoveSpeed(self.handle)
end

-- 获取实际攻击间隔
function mt:get_attack_cool()
	if self:get_attack_speed() <= 0 then
		return 0
	end
	return math.max(self:get("攻击间隔"), 0.1) * (100 / self:get_attack_speed())
end

function mt:get_shield()
	return self:get("护盾")
end

function mt:set_shield(value)
	return self:set("护盾", value)
end

function mt:add_shield(value)
	self:set_shield(self:get_shield() + value)
end

return {
	attribute = attribute,
	set = set,
	get = get,
	on_set = on_set,
	on_add = on_add,
	on_get = on_get,
}
